package com.slaffka.model;

import org.hibernate.annotations.Filter;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.Filters;
import org.hibernate.annotations.ParamDef;
import org.jboss.solder.core.Veto;

import javax.persistence.*;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * Created with IntelliJ IDEA.
 * User: slaffka
 * Date: 04.11.12
 * Time: 13:59
 * To change this template use File | Settings | File Templates.
 */
@Entity
@Table(name = "loan")
@Veto
//@FilterDef(name="currentFilial", parameters=@ParamDef( name="currentFilial", type="long" ) )
//@Filters({
//        @Filter(name="currentFilial", condition="(filial_id = :currentFilial or :currentFilial = 1)")
//})
public class Loan implements Serializable {
    private Long contractId;
    private User owner;
    private Date createDate;
    private Integer period;
    private Double amount;
    private Debtor debtor;
    private Date finishDate;
    private Double finishAmount;
    private Filial filial;

    @Id
    @Column(name = "contract_id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    public Long getContractId() {
        return contractId;
    }

    public void setContractId(Long contractId) {
        this.contractId = contractId;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "owner_id")
    public User getOwner() {
        return owner;
    }

    public void setOwner(User owner) {
        this.owner = owner;
    }

    @Column(name = "create_date")
    @Temporal(value = TemporalType.DATE)
    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public Integer getPeriod() {
        return period;
    }

    public void setPeriod(Integer period) {
        this.period = period;
    }

    public Double getAmount() {
        return amount;
    }

    public void setAmount(Double amount) {
        this.amount = amount;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "debtor_id", nullable = false)
    public Debtor getDebtor() {
        return debtor;
    }

    public void setDebtor(Debtor debtor) {
        this.debtor = debtor;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "filial_id")
    public Filial getFilial() {
        return filial;
    }

    public void setFilial(Filial filial) {
        this.filial = filial;
    }

    /**
     * Возвращает дату возврата денег по договору из расчета Дата заключения договора + Срок займа
     * @return планируемую дату возврата долга
     */
    @Transient
    public Date getPlannedDate(){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(createDate);
        calendar.add(Calendar.DATE, period);
        return calendar.getTime();
    }

    @Transient
    public String getPlannedDateStr(){
        Date plannedDate = getPlannedDate();
        DateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
        return formatter.format(plannedDate);
    }

    @Transient
    public String getCreateDateStr(){
        DateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
        return formatter.format(createDate);
    }

    /**
     * Метод расчитывает планируемую сумму долга
     * @return планируемую сумму долга
     */
    @Transient
    public Double getPlannedAmount(){
        return amount + getFeeForUseOfFunds();
    }

    @Transient
    public boolean isLoanOverdue(){
        Date today = getToday();
        return today.after(getPlannedDate());
    }

    @Transient
    private Date getToday() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * Метод возвращает количество просроченных дней
     * @return количество просроченных дней или 0 если займ не просрочен
     */
    @Transient
    public long getOverdueDays(){
        long overdueDayes = 0;
        if (isLoanOverdue()){
             overdueDayes = (getToday().getTime() - getPlannedDate().getTime()) / (1000 * 60 * 60 * 24);
        }
        return overdueDayes;
    }

    /**
     * Метод расчитывает текущую сумму долга с учётом штрафов
     * @return
     */
    @Transient
    public Double getCurrentAmount(){
        if (finishAmount != null){
            return finishAmount;
        }
        return getPlannedAmount() + (getOverdueDays() * 200);
    }

    public void setFinishDate(Date finishDate) {
        this.finishDate = finishDate;
    }

    /**
     * Метод рассчитывает планируемую плату за пользование денежными средствами (без учета штрафов)
     * @return
     */
    @Transient
    public Double getFeeForUseOfFunds(){
        return amount * period * 0.02;
    }

    /**
     * Метод рассчитывает плату за пользование денежными средствами с учётом штрафов
     * @return
     */
    @Transient
    public Double getFactFeeForUseOfFunds(){
        return getCurrentAmount() - getAmount();
    }

    /**
     *
     * @return метод возвращает завершен ли займ
     */
    @Transient
    public boolean isFinished(){
        return finishDate != null;
    }

    /**
     * Фактическая дата погашения долга
     * @return если займ активен, то null; иначе фактическую дату погашения задолженности
     */
    @Column(name = "finish_date", nullable = true)
    @Temporal(value = TemporalType.DATE)
    public Date getFinishDate() {
        return finishDate;
    }

    /**
     * Фактическая сумма долга. Т.е. сколько клиент отдал денег чтобы завершить договор.
     * @return если займ активен, то null; иначе фактическую погашенную сумму
     */
    @Column(name = "finish_amount", nullable = true)
    public Double getFinishAmount() {
        return finishAmount;
    }

    public void setFinishAmount(Double finishAmount) {
        this.finishAmount = finishAmount;
    }

    @Override
    public String toString() {
        return "Loan{" +
                "contractId=" + contractId +
                ", owner=" + owner +
                ", createDate=" + createDate +
                ", period=" + period +
                ", amount=" + amount +
                ", debtor=" + debtor +
                ", finishDate=" + finishDate +
                ", finishAmount=" + finishAmount +
                ", filial=" + filial +
                '}';
    }
}
